/*
 * $Id: RuntimeCheckInterceptorCodeGenerator.java 2233 2007-04-18 14:49:48Z kofron $
 *
 * Behavior Protocols extensions for static and runtime checking
 * developed for the Julia implementation of Fractal.
 *
 * Copyright (C) 2006
 *    Formal Methods In Software Engineering Group
 *    Institute of Computer Science
 *    Academy of Sciences of the Czech Republic
 *
 * Copyright (C) 2006 France Telecom
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 * Contact: ft@nenya.ms.mff.cuni.cz
 * Authors: Vladimir Mencl <mencl@nenya.ms.mff.cuni.cz>
 *
 * Created on Dec 6, 2004
 *
 */

package org.objectweb.fractal.bpc.julia;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.fractal.bpc.IdentityAwareInterceptor;
import org.objectweb.fractal.bpc.RuntimeCheckController;
import org.objectweb.fractal.bpc.RuntimeCheckInterceptor;
import org.objectweb.fractal.julia.InitializationContext;
import org.objectweb.fractal.julia.asm.AbstractCodeGenerator;
import org.objectweb.fractal.julia.asm.ClassGenerationException;
import org.objectweb.fractal.julia.asm.InterceptorClassGenerator;
import org.objectweb.fractal.julia.asm.InterceptorCodeAdapter;

/**
 * generates interceptors implementing the
 * {@link org.objectweb.fractal.bpc.RuntimeCheckInterceptor}
 * interface that interact with
 * {@link org.objectweb.fractal.bpc.RuntimeCheckController}.
 * 
 * These interceptors provide notifications on entering and leaving method calls
 * on component interfaces.
 * 
 * @author Vladimir Mencl
 *  
 */

public class RuntimeCheckInterceptorCodeGenerator extends AbstractCodeGenerator {

	public String controllerInterfaceName = "runtimecheck-controller";

	private InterceptorClassGenerator icg = null;

	private String delegateFieldName = "fcRtcDelegate";

	private String delegateFieldType = Type
			.getInternalName(RuntimeCheckController.class);

	private String delegateFieldDesc = "L" + delegateFieldType + ";";

	private String instanceRefDesc = "L"
			+ Type
					.getInternalName(org.objectweb.fractal.julia.ComponentInterface.class)
			+ ";";

	public void generateInitCode(MethodVisitor cv)
			throws ClassGenerationException {
		super.generateInitCode(cv); // does nothing in AbstractCodeGenerator

		// formerly done by SimpleCodeGenerator: add a controller field +
		// initialize it
		// ... much easier for a known controller type

		icg.cw.visitField(ACC_PRIVATE, delegateFieldName, delegateFieldDesc,
				null, null);

		// I want: fcRtcDelegate =
		// (RuntimeCheckController)initializationContext.getInterface("runtimecheck-controller")
		cv.visitVarInsn(ALOAD, 0); // push this - for use with PUTFIELD
		cv.visitVarInsn(ALOAD, 1); // push param1 - InitializationContext
		cv.visitLdcInsn(controllerInterfaceName); // push itf-name
		cv.visitMethodInsn(INVOKEVIRTUAL, Type
				.getInternalName(InitializationContext.class), "getInterface",
				"(Ljava/lang/String;)Ljava/lang/Object;");
		cv.visitTypeInsn(CHECKCAST, delegateFieldType);
		cv.visitFieldInsn(PUTFIELD, icg.name, delegateFieldName,
				delegateFieldDesc);

		/*
		 * generate fields & methods to implement the RuntimeCheckInterceptor
		 * interface private String itfInstanceName; private boolean
		 * itfIsClient; private ComponentInterface itfInstanceRef;
		 */

		icg.cw.visitField(ACC_PRIVATE, "fcItfInstanceName", "Ljava/lang/String;",
				null, null);
		icg.cw.visitField(ACC_PRIVATE, "fcItfIsClient", "Z", null, null);
		icg.cw.visitField(ACC_PRIVATE, "fcItfInstanceRef", instanceRefDesc, null,
				null);

		/*
		 * more fun: now the methods public String getItfInstanceName() { return
		 * itfInstanceName; } public ComponentInterface getItfInstanceRef() {
		 * return itfInstanceRef; } public void
		 * setItfInstanceRef(ComponentInterface itfRef) { itfInstanceRef =
		 * itfRef; } public void setItfInstanceName(String itfInstanceName) {
		 * this.itfInstanceName = itfInstanceName; } public boolean
		 * isItfIsClient() { return itfIsClient; } public void
		 * setItfIsClient(boolean itfIsClient) { this.itfIsClient = itfIsClient; }
		 */
		generateGetSetMethods("fcItfInstanceName", "Ljava/lang/String;");
		generateGetSetMethods("fcItfIsClient", "Z");
		generateGetSetMethods("fcItfInstanceRef", instanceRefDesc);
	}

	private static int[] retInsns = { ARETURN, IRETURN, FRETURN, LRETURN,
			DRETURN };

	private static int[] loadInsns = { ALOAD, ILOAD, FLOAD, LLOAD, DLOAD };

	private static int[] storeInsns = { ASTORE, ISTORE, FSTORE, LSTORE, DSTORE };

	private int getInsnIdx(String fieldDesc) throws ClassGenerationException {
		int insnIdx;

		if ((fieldDesc.length() == 1) && "BCISZ".indexOf(fieldDesc) > -1)
			insnIdx = 1;
		else if (fieldDesc.equals("F"))
			insnIdx = 2;
		else if (fieldDesc.equals("J"))
			insnIdx = 3;
		else if (fieldDesc.equals("D"))
			insnIdx = 4;
		else if (fieldDesc.startsWith("L"))
			insnIdx = 0;
		else
			throw new ClassGenerationException(null, icg.args.toString(),
					"invalid getSet field type: " + fieldDesc);

		return insnIdx;
	}

	private void generateGetSetMethods(String fieldName, String fieldDesc)
			throws ClassGenerationException {
		String getMethodDesc = "()" + fieldDesc;
		String setMethodDesc = "(" + fieldDesc + ")V";
		String methodNameBase = fieldName.substring(0, 1).toUpperCase()
				+ fieldName.substring(1);
		int insnIdx = getInsnIdx(fieldDesc);

		MethodVisitor cv1 = icg.cw.visitMethod(ACC_PUBLIC,
				"get" + methodNameBase, getMethodDesc, null, null);
		cv1.visitVarInsn(ALOAD, 0); /* push(this) " */
		cv1.visitFieldInsn(GETFIELD, icg.name, fieldName, fieldDesc);
		cv1.visitInsn(retInsns[insnIdx]);
		cv1.visitMaxs(1, 0); // ??? should maxStack be increased for long and
							 // double?

		MethodVisitor cv2 = icg.cw.visitMethod(ACC_PUBLIC,
				"set" + methodNameBase, setMethodDesc, null, null);
		cv2.visitVarInsn(ALOAD, 0); /* push(this) " */
		cv2.visitVarInsn(loadInsns[insnIdx], 1);
		cv2.visitFieldInsn(PUTFIELD, icg.name, fieldName, fieldDesc);
		cv2.visitInsn(RETURN);
		cv2.visitMaxs(1, 1 + (insnIdx >= 4 ? 1 : 0));
	}

	protected String getMethodName(Method m) {
		return m.getName(); // ignoring parameter types
	}

	protected int getInterceptionType(Method m) {
		return InterceptorCodeAdapter.FINALLY;
	}

	protected void generateInterceptionCodeBlock(Method m, boolean pre,
			MethodVisitor cv, int formals) {

		// desired code:
		// fcRtcDelegate.[enter/leave]Method(itfInstanceName, "main",
		// itfIsClient, null);

		// push delegate controller reference
		//cv.visitInsn(ACONST_NULL);
		cv.visitVarInsn(ALOAD, 0);
		cv.visitFieldInsn(GETFIELD, icg.name, delegateFieldName,
				delegateFieldDesc);

		// push itfname
		//cv.visitInsn(ACONST_NULL);
		cv.visitVarInsn(ALOAD, 0);
		cv.visitFieldInsn(GETFIELD, icg.name, "fcItfInstanceName",
				"Ljava/lang/String;");

		// push the name of the intercepted method
		cv.visitLdcInsn(getMethodName(m));

		// push boolean parameter itfIsClient
		//cv.visitInsn(ICONST_0);
		cv.visitVarInsn(ALOAD, 0);
		cv.visitFieldInsn(GETFIELD, icg.name, "fcItfIsClient", "Z");

		// push "null" for "Object params[]" -- could capture params in the
		// future
		cv.visitInsn(ACONST_NULL);

		// now we generate the code to call the pre or post method

		String delegMethodName = pre ? "enterFcMethod" : "leaveFcMethod"; 
		String methodDescr = "(Ljava/lang/String;Ljava/lang/String;Z[Ljava/lang/Object;)V";

		// we can finally generate the code to invoke the pre or post method
		cv.visitMethodInsn(INVOKEINTERFACE, delegateFieldType, delegMethodName,
				methodDescr);
	}

	public void generateCopyVar(MethodVisitor cv, String fieldName,
			String fieldDesc) throws ClassGenerationException {
		//int insnIdx=getInsnIdx(fieldDesc);

		cv.visitVarInsn(ALOAD, 1); /* push(clone-ref) " */
		cv.visitVarInsn(ALOAD, 0); /* push(this) " */
		cv.visitFieldInsn(GETFIELD, icg.name, fieldName, fieldDesc);
		cv.visitFieldInsn(PUTFIELD, icg.name, fieldName, fieldDesc);
	}

	public void generateCloneCode(MethodVisitor cv)
			throws ClassGenerationException {
		super.generateCloneCode(cv); // does nothing

		// copy field: delegate
		generateCopyVar(cv, delegateFieldName, delegateFieldDesc);
		generateCopyVar(cv, "fcItfInstanceName", "Ljava/lang/String;");
		generateCopyVar(cv, "fcItfIsClient", "Z");
		generateCopyVar(cv, "fcItfInstanceRef", instanceRefDesc);
	}

	/**
	 * Returns the list of interfaces to be implemented by the generated
	 * interceptor.
	 * <p>
	 * 
	 * This method adds the {@link RuntimeCheckInterceptor}interface required
	 * by the interceptor this class generates.
	 * <p>
	 * 
	 * This method implementation expects that the getImplementedInterfaces()
	 * method has been added to the
	 * {@link org.objectweb.fractal.julia.asm.CodeGenerator}interface.
	 * <p>
	 * 
	 * If not, the getImplementedInterfaces() method in
	 * {@link RuntimeCheckInterceptorClassGenerator}has to be used instead.
	 * <p>
	 * Implementation note: to make this source code compile with Julia
	 * distributions not including the CodeGenerator#getImplementedInterfaces()
	 * extension, remove the call to super.getImplementedInterfaces() - which
	 * should return an empty list anyway.
	 * 
	 * @see RuntimeCheckInterceptor
	 * @see RuntimeCheckInterceptorClassGenerator#getImplementedInterfaces()
	 * @see org.objectweb.fractal.julia.asm.CodeGenerator#getImplementedInterfaces()
	 */
	public List getImplementedInterfaces() throws ClassGenerationException {
		List itfs = null;
		//itfs = super.getImplementedInterfaces();
		if (itfs == null)
			itfs = new ArrayList();
		String itfName = org.objectweb.asm.Type
				.getInternalName(org.objectweb.fractal.bpc.RuntimeCheckInterceptor.class);
		if (!itfs.contains(itfName))
			itfs.add(itfName);
		String itfName2 = org.objectweb.asm.Type
				.getInternalName(IdentityAwareInterceptor.class);
		if (!itfs.contains(itfName2))
			itfs.add(itfName2);
		//itfs.add(Type.getInternalName(RuntimeCheckInterceptor.class));
		return itfs;
	}

	/*
	 * @see org.objectweb.fractal.julia.asm.CodeGenerator#init(org.objectweb.fractal.julia.asm.InterceptorClassGenerator)
	 */
	public int init(InterceptorClassGenerator icg) {
		super.init(icg);
		this.icg = icg;
		return IN_OUT;
	}
}
